package com.yhh;

import com.yhh.Entity.Command;
import com.yhh.Entity.HandlerCommand;
import com.yhh.Exceptions.ExitException;
import com.yhh.Exceptions.InstructionException;
import com.yhh.Exceptions.LocalCommandException;
import com.yhh.Exceptions.NonInstructionException;
import com.yhh.myUtils.CommandUtils;
import com.yhh.myUtils.myStringUtils;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class ClientThread {
    public static final String directory = System.getProperty("user.dir") + File.separatorChar + "RecvCache";
    //输入scanner
    private final Scanner scanner = new Scanner(System.in);
    //首次与外网主机通信的连接
    private Socket socket;
    //    private PrintWriter bw; 1.8不能指定字符集
    private BufferedReader br;
    private BufferedWriter bw;
    //    private final String ip = "127.0.0.1";
    private final String ip;
    private final int port;
    private final CommandUtils commandUtils = CommandUtils.getCommandUtils();

    public static String uncheckFilename = null;

    public static String uncheckKey = null;

    private HashMap<String, HandlerCommand> commandMap = new HashMap<>();

    private boolean isWait = false;

    public ClientThread(String url, int port) {
        this.ip = url;
        this.port = port;
    }

    private void init() {
        commandMap.put("send", new SendCommand(socket));
        commandMap.put("recv", new RecvCommand(socket));
        commandMap.put("help", new HelpCommand(socket));
        commandMap.put("show", new ShowCommand(socket));
        commandMap.put("clear", new ClearCommand(socket));
    }

    public void start() {

        try {
            // 新建一个socket通道
            socket = new Socket();

            socket.connect(new InetSocketAddress(ip, port));
            System.out.println("请输入命令 send -f filename 或 recv -m key ;输入help -a获取帮助");

            bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
            br = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
            //初始化指令
            init();
            //检查是否有未传输完毕的文件
            checkUnDoneFile();
            try {
                sendMsg(myStringUtils.getCode());
                while (true) {
                    if (!isWait) {
                        String in;
                        System.out.print("请输入：");
                        //获取输入
                        in = scanner.nextLine();
                        isWait = true;
                        //处理命令
                        if (handlerCommand(in)) {
                            break;
                        }
                        isWait = false;
                    }
                }
            } finally {
                System.out.println("连接关闭");
                // 关闭资源
                try {
                    if (bw != null) {
                        bw.close();
                    }
                    if (br != null) {
                        br.close();
                    }
                    if (socket != null) {
                        socket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //检查是否存在未接受完成的文件
    private void checkUnDoneFile() {
        File recvCache = new File(System.getProperty("user.dir") + File.separatorChar + "recvCacheFile");
        if (recvCache.exists()) {
            FileInputStream fisRecv = null;
            BufferedReader brRecv = null;
            try {
                fisRecv = new FileInputStream(recvCache);
                brRecv = new BufferedReader(new InputStreamReader(fisRecv));
                uncheckFilename = brRecv.readLine();
                uncheckKey = brRecv.readLine();
                try {
                    fisRecv.close();
                    brRecv.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                File file = new File(directory + File.separatorChar + uncheckFilename);
                if (!file.exists()) {
                    if (recvCache.delete()) {
                        System.out.println("已删除");
                    }
                    return;
                }
                System.out.println("文件名为:" + uncheckFilename + " key为:" + uncheckKey + " 的文件传输出现异常。可使用recv -m -r 重新传输这段文件");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fisRecv != null) fisRecv.close();
                    if (brRecv != null) brRecv.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    }

    /*
     * 处理与服务器连接的交互，返回是否退出
     */
    public boolean handlerCommand(String in) {
        try {
            //提取参数
            Map<String, String> args = CommandUtils.processCommand(in);
            //检查
            HandlerCommand command = commandMap.get(args.get("start"));

            if (!commandUtils.checkCommandParameters(args, (Command) command)) {
                return false;
            }
            //检查完毕，没有错误，不是本地指令，发送指令到服务端
            sendMsg(in);
            //处理指令
            command.handlerCommand(args);
        } catch (NonInstructionException e) {
            System.out.println("无效的参数");
        } catch (InstructionException e) {
            System.out.println(e);
            System.out.println("尝试输入help -h " + e.getInstruction() + " 获取帮助");
        } catch (ExitException e) {
            System.out.println("88");
            return true;
        } catch (LocalCommandException e) {
            //本地指令
            return false;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    //封装接受操作函数，增加可读性
    private String recvMsg() {
        String msg = null;
        try {
            msg = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return msg;
    }

    //封装接受函数
    private void sendMsg(String success) {
        try {
            bw.write(success + "\n");
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
